

package tomkitty;

import java.util.*;
import java.util.jar.*;
import java.io.*;

public class TkAppManager extends Turdlet implements AppManager {

  public static final String SYSTEM_APP = "system";

  protected Configuration config;
  protected static Hashtable<String, AppContext> apps = null;
  protected JarFile currentJar;
  protected MyClassLoader cl;
  
  protected class MyClassLoader extends ClassLoader {
    
    public Class<?> defClass(String name, byte[] b, int off, int len) {
      return defineClass(name, b, off, len);
    }

    public void reslvClass(Class<?> c) {
      resolveClass(c);
    }

  }

  public TkAppManager() {
    cl = new MyClassLoader();
  }

  public Handler init(Handler handler, AppContext context) {
    super.init(handler, context);

    this.config = context.getConfig();

    if (apps == null) {
      apps = new Hashtable<String, AppContext>();
      loadApps(config.get(CONF_CLASSPATH));
    }

    return this;
  }

  protected void loadApps(String classpath) {
    if (classpath == null) {
      return;
    }

    // load in classes and stuff

    Debug.p("Loading apps:");

    String[] scanDirs = classpath.split(File.pathSeparator);
    
    for (int z = 0; z < scanDirs.length; z++) {

      File scanDir = new File(scanDirs[z]);
      File[] files;
      
      // has to be a directory or a jar file
      if(scanDir.exists() && scanDir.isDirectory()) {

        // get a list of jar files in the directory, using an anonymous inner 
        // FileFilter
        files = scanDir.listFiles(
          new FileFilter() {
            public boolean accept(File scanDir) {
              String name = null;
              if(scanDir != null) {
                name = scanDir.getName();
              }
              return name.endsWith(".jar");
            }
          }
        );
      } else if (scanDir.exists() && scanDir.getName().endsWith(".jar")) {
        files = new File[1];
        files[0] = scanDir;
      } else {
        continue;
      }
      
      // for each jar file, try and load it up
      for(int i = 0; i < files.length; i++) {
        try {
          currentJar = new JarFile(files[i]);
          loadFromStream(new JarInputStream(new FileInputStream(files[i])),
              files[i]);
        } catch (FileNotFoundException fnf) {
          fnf.printStackTrace();
        } catch (IOException ioe) {
          ioe.printStackTrace();
        }
      }

    }
    Debug.p("");

    AppContext ac = new TkAppContext();
    ac.setConfig(new TkConfiguration(config));
    ac.setRegisteredData(new TkRegisteredData());
    apps.put(SYSTEM_APP, ac);
  }

  public void loadFromStream(JarInputStream jIs, File jarfile) {

    try {
      for(JarEntry jEntry; (jEntry = jIs.getNextJarEntry()) != null; 
          jIs.closeEntry()) {
        if(jEntry.isDirectory()) {
          loadFromStream(new JarInputStream(currentJar.getInputStream(jEntry)),
              jarfile);
        } else {
          String name = jEntry.getName();

          if(name.endsWith(".class")) {

            int start = currentJar.getName().lastIndexOf(File.separator) + 1;
            int end   = currentJar.getName().indexOf(".jar");

            String appName = currentJar.getName().substring(start, end);

            if (appName.equals("tomkitty")) {
              throw new TkClassNotFoundException();
            }

            // It's a class, let's load it up
            InputStream in = new BufferedInputStream(
                currentJar.getInputStream(jEntry));

            ByteArrayOutputStream baOs = new ByteArrayOutputStream();
            
            int read;
            while((read = in.read()) != -1)  {
              baOs.write(read);
            }
            
            Class tmpClass = 
              cl.defClass(null, baOs.toByteArray(), 0, baOs.size());
            cl.reslvClass(tmpClass);
        
            String fullName = tmpClass.getName(); //bob.wtf.test.Test...
            String simplifiedName = 
              fullName.substring(fullName.lastIndexOf('.')+1);

            AppContext ac;

            if (! apps.containsKey(appName)) {
              ac = new TkAppContext();
              ac.setConfig(new TkConfiguration(config));

              String appconf = "";
              if (jarfile.getParent() != null) {
                appconf = jarfile.getParent()+File.separator+appName+".conf";
                if ( (new File(appconf)).exists() ) {
                  ac.getConfig().loadConfFromXML(appconf);
                }
              }

              ac.setRegisteredData(new TkRegisteredData());
              apps.put(appName, ac);
            } else {
              ac = apps.get(appName);
            }

            if (Handler.class.isAssignableFrom(tmpClass)) {
              ac.setClass(simplifiedName, tmpClass);
              Debug.p("loaded '/"+appName+"/"+simplifiedName+"'");
            } else {
              // nothing
            }
          }
        }
      }
    } catch (TkClassNotFoundException tkcnf_e) {
    } catch (LinkageError lnk_e) {
    } catch (IOException io_e) {
      io_e.printStackTrace();
    }
  }

  public void unloadApps(String appname) {
    apps.remove(appname);
  }

  public void doit() {
    Handler t = null;
    AppContext ac = null;
    String path = "";
    String[] segments;

    try {
      path = request.getUri().getPath();
      while (path.startsWith("/")) {
        if (path.length() > 1) {
          path = path.substring(1);
        } else {
          path = "";
        }
      }
      segments = path.split("/");

      if (segments.length < 1 || (ac = apps.get(segments[0])) == null) {
        String dfl = config.get(CONF_DEFAULT_APP);
        if (dfl == null || (ac = apps.get(dfl)) == null) {
          throw new TkClassNotFoundException();
        } else {
          String query = request.getOrigUri().getQuery();
          String fragment = request.getOrigUri().getFragment();
          String newpath =   request.getOrigUri().getScheme() + "://" + 
                            request.getOrigUri().getAuthority() + 
                            "/" + dfl + request.getOrigUri().getPath() +
                            (query != null ? "?" + query : "") +
                            (fragment != null ? "#" + fragment : "");
          response.setField("Location", newpath);
          response.setStatus(HTTP_MOVED_PERM);
        }
      }

      handler.setContext(ac);
    } catch (TkClassNotFoundException e) {
      response.setStatus(HTTP_NOT_FOUND);
    } catch (Exception ee) {
      response.setStatus(HTTP_SERVER_ERROR);
    }
  }

}
